<?php
namespace Core\Libs\Cast;

class Cast {

	private $variable = NULL;
	private $variableType = '';
	private $numArgs = 0;
	private $inputType = '';
	private $cents = ',';
	private $thousands = '.';
	private $currency = 'R$';
	private $country = 'pt_BR';
	private $precision = 2;
	private $strict = FALSE;
	private $throws = TRUE;

	public function __construct($variable = NULL) {

		$this->variable = $variable;
		$this->inputType = '';
		$this->cents = ',';
		$this->thousands = '.';
		$this->currency = 'R$';
		$this->country = \Core\Uses\Ler('Core.lang.internal');
		$this->precision = 2;
		$this->strict = FALSE;
		$this->throws = TRUE;
		$this->numArgs =& func_num_args();
		$this->variableType =& gettype(&$this->variable);
	}

	final public function setCountry($country = 'pt_BR') {

		$this->country = $country;
		return $this;
	}

	final public function getCountry() {

		return $this->country;
	}

	final public function setStrict($strict = FALSE) {

		$this->strict = $strict;
		return $this;
	}

	final public function getStrict() {

		return $this->strict;
	}

	final public function &getVariable() {

		return $this->variable;
	}

	final public function &getVariableType() {

		return $this->variableType;
	}

	final public function &setThrows($throws, $return = TRUE) {

		$this->throws =& $throws;

		if($return) {

			return $this;
		}
	}

	final public function getThrows() {

		return $this->throws;
	}

	final private function throwException() {

		if($this->throws === TRUE) {

			$arrayParams =& func_get_args();
			$message = '';

			if(func_num_args() > 1) {

				if($arrayParams[1] == 'money') {

					$arrayParams[0] = '';
				}

				$message = 'Tipos incompatíveis, não foi possível efetuar o cast da variável.<br>'.
						   'Variável: '.$arrayParams[0].'<br>'.
						   'Para o tipo: '.$arrayParams[1].'<br>';
			}

			else {

				$message =& $arrayParams[0];
			}

			unset($arrayParams);
			throw \Core\Uses\Exception($message, E_WARNING, 100);
		}

		return NULL;
	}

	final public function noEmpty($variable = NULL, $strict = FALSE) {

		$numArgs =& func_num_args();
		$return = FALSE;

		if($numArgs < 1) {

			$variable = $this->variable;
		}

		elseif($numArgs === 1) {

			$strict = $this->strict;
		}

		if(is_array($variable) && count($variable) < 1) {

			$return = TRUE;
		}

		elseif((!$strict && $this->equals(NULL, $variable)) || ($strict && empty($variable))) {

			$return = TRUE;
		}

		if($return === TRUE && $this->throws === TRUE) {

			$this->throwException('Error: Variable is empty.');
		}

		else {

			return $return;
		}
	}

	final public function casting($variable, $type = 'string', $lenght = 1, $multiByte = TRUE) {

		$temp = 0;

		if($type === 'integer' || $type === 'int') {

			$temp = $variable;
			$variable = (int)$variable;

			if($variable === 0 && $temp !== 0 && $temp != '0' && $this->throws === TRUE) {

				return $this->throwException(&$variable, &$type);
			}

			else {

				return $variable;
			}
		}

		elseif($type === 'money') {

			$variable =& preg_replace('/^'.\Core\Uses\addSlashes($this->currency).'(\ *)/i', '', &$variable);

			if(is_numeric(&$variable) || $this->throws === FALSE) {

				$variable =& number_format(&$variable, &$this->precision, &$this->cents, &$this->thousands);
				return $this->currency.$variable;
			}

			return $this->throwException(&$variable, &$type);
		}

		elseif($type === 'double' || $type === 'numeric' || $type === 'float') {

			if($this->inputType === 'money') {

				$integerPart = '';
				$decimalPart = '';
				$variable =& preg_replace('/^'.\Core\Uses\addSlashes($this->currency).'(\ *)/i', '', &$variable);

				if(strpos(&$variable, &$this->cents) !== FALSE) {

					list($decimalPart, $integerPart) = explode(&$this->cents, strrev(&$variable), 2);
					$variable = str_replace(&$this->thousands, '', strrev(&$integerPart)).'.'.$decimalPart;
				}
			}

			if(is_numeric(&$variable) || $this->throws === FALSE) {

				return (double)$variable;
			}

			return $this->throwException(&$variable, &$type);
		}

		elseif($type === 'boolean' || $type === 'bool') {

			if(is_bool(&$variable)) {

				return $variable;
			}

			else {

				if(is_numeric(&$variable)) {

					$variable = (double)$variable;

					if($variable == 1) {

						$variable = TRUE;
						return $variable;
					}

					elseif($variable == 0) {

						$variable = FALSE;
						return $variable;
					}

					elseif($this->throws === TRUE) {

						return $this->throwException(&$variable, &$type);
					}
				}

				elseif(is_string(&$variable)) {

					$variable =& strtolower(&$variable);

					if($variable === 'true') {

						$variable = TRUE;
						return $variable;
					}

					elseif($variable === 'false') {

						$variable = FALSE;
						return $variable;
					}

					elseif($this->throws === TRUE) {

						return $this->throwException(&$variable, &$type);
					}
				}
			}

			if($this->throws === TRUE) {

				return $this->throwException(&$variable, &$type);
			}
		}

		elseif($type === 'string') {

			if($variable === FALSE) {

				$variable = '0';
				return $variable;
			}

			elseif(is_object(&$variable)) {

				if(method_exists(&$variable, '__toString')) {

					return $variable->__toString();
				}

				return get_class(&$variable);
			}

			elseif(is_array(&$variable) && $this->throws === TRUE) {

				return $this->throwException(&$variable, &$type);
			}

			elseif(is_resource(&$variable)) {

				return get_resource_type(&$variable);
			}

			return (string)$variable;
		}

		elseif($type === 'char') {

			if($multiByte) {

				return mb_substr($this->string($variable), 0, &$lenght);
			}

			return substr($this->string($variable), 0, &$lenght);
		}

		elseif($type === 'array') {

			return (array)$variable;
		}

		elseif($type === 'object') {

			return (object)$variable;
		}

		elseif($type === 'null' || $type === 'NULL') {

			if(is_null(&$variable)) {

				return $variable;
			}

			elseif(is_string(&$variable)) {

				$variable =& strtolower(&$variable);

				if($variable === 'null' || empty($variable)) {

					$variable = NULL;
					return $variable;
				}

				return $variable;
			}

			elseif($this->throws === TRUE) {

				return $this->throwException(&$variable, &$type);
			}

			$variable = FALSE;
			return $variable;
		}
	}

	final public function integer($variable) {

		return $this->int($variable);
	}

	final public function int($variable) {

		return $this->casting($variable, 'integer');
	}

	final public function money($variable, $cents = NULL, $thousands = NULL, $currency = NULL, $precision = 2) {

		$numArgs =& func_num_args();

		if($numArgs < 5) {

			if(empty($this->country)) {

				$this->country = 'pt_BR';
			}

			$arrayConfig = \Core\Uses\Ler('core.lang.' . $this->country . '.format.money', TRUE, 'array');

			switch(TRUE) {

				case $numArgs === 1:

					$currency = $arrayConfig['currency']['_value'];
					$thousands = $arrayConfig['separator']['thousands']['_value'];
					$cents = $arrayConfig['separator']['cents']['_value'];
					$precision = $arrayConfig['precision']['_value'];
					break;
				case $numArgs === 2:

					$thousands = $arrayConfig['separator']['thousands']['_value'];
					$currency = $arrayConfig['currency']['_value'];
					$precision = $arrayConfig['precision']['_value'];
					break;
				case $numArgs === 3:

					$currency = $arrayConfig['currency']['_value'];
					$precision = $arrayConfig['precision']['_value'];
					break;
				case $numArgs === 4:

					$precision = $arrayConfig['precision']['_value'];
					break;
			}
		}

		if(!is_numeric($variable)) {

			if(preg_match('/^'.\Core\Uses\addSlashes($currency).'(\ *)/i', $variable) || $flag = strpos($variable, $cents) !== FALSE) {

				$integerPart = '';
				$decimalPart = '';
				$variable = preg_replace('/^'.\Core\Uses\addSlashes($currency).'(\ *)/i', '', $variable);

				if($flag !== FALSE) {

					list($decimalPart, $integerPart) = explode($cents, strrev($variable), 2);
					$variable = str_replace($thousands, '', strrev($integerPart)).'.'.$decimalPart;
				}
			}
		}

		$this->currency = $currency;
		$this->thousands = $thousands;
		$this->cents = $cents;
		$this->precision = $precision;

		return $this->casting($variable, 'money');
	}

	final public function numeric($variable, $cents = NULL, $thousands = NULL, $currency = NULL) {

		return $this->double($variable, $cents, $thousands, $currency);
	}

	final public function decimal($variable, $cents = NULL, $thousands = NULL, $currency = NULL) {

		return $this->double($variable, $cents, $thousands, $currency);
	}

	final public function double($variable, $cents = NULL, $thousands = NULL, $currency = NULL) {

		$numArgs =& func_num_args();
		$arrayConfig = \Core\Uses\Ler('core.format.money', TRUE, 'array');

		switch(TRUE) {

			case ($numArgs === 1):

				$thousands = $arrayConfig['separator']['thousands']['_value'];
				$cents = $arrayConfig['separator']['cents']['_value'];
				$currency = $arrayConfig['currency']['_value'];					
				break;
			case ($numArgs === 2):

				$thousands = $arrayConfig['separator']['thousands']['_value'];
				$currency = $arrayConfig['currency']['_value'];
				break;
			case $numArgs === 3:

				$currency = $arrayConfig['currency']['_value'];
				break;
		}

		if(strpos(&$variable, &$cents) !== FALSE) {

			$this->inputType = 'money';
		}

		$this->cents =& $cents;
		$this->thousands =& $thousands;
		$this->currency =& $currency;

		return $this->casting($variable, 'double');
	}

	final public function float($variable, $cents = NULL, $thousands = NULL, $currency = NULL) {

		return $this->double($variable, $cents, $thousands, $currency);
	}

	final public function string($variable) {

		return $this->casting($variable, 'string');
	}

	final public function char($variable, $lenght = 1, $multiByte = TRUE) {

		return $this->casting($variable, 'char', &$lenght, &$multiByte);
	}

	final public function bool($variable) {

		return $this->casting($variable, 'boolean');
	}

	final public function boolean($variable) {

		return $this->bool($variable);
	}

	final public function toArray($variable) {

		return $this->casting($variable, 'array');
	}

	final public function toObject($variable) {

		return $this->casting($variable, 'object');
	}

	final public function compareElementsTo($arrayElements = array(), $typeToCompare = '==') {

		
	}

	final public function compareElements($arrayElements = array(), $typeToCompare = '==', $comparableTo, $reference = FALSE) {

		if($reference === FALSE) {

			foreach($arrayElements as $idx => $element) {

				if($this->compare($element, $typeToCompare, $comparableTo, FALSE) === FALSE) {

					unset($arrayElements, $comparableTo);

					if($this->throws === TRUE) {
	
						return $this->throwException('Error: Variable is not equal to: ' . $typeToCompare . 'in INDEX: ' . $idx . ' - OBS: Sent Array of elements to method compareElements.');
					}

					return FALSE;
				}
			}
		}

		else {

			foreach($arrayElements as $idx => &$element) {

				if($this->compare(&$element, &$typeToCompare, &$comparableTo, FALSE) === FALSE) {

					unset($arrayElements, $comparableTo);

					if($this->throws === TRUE) {
	
						return $this->throwException('Error: Variable is not equal to: ' . $typeToCompare . 'in INDEX: ' . $idx . ' - OBS: Sent Array of elements to method compareElements.');
					}

					return FALSE;
				}
			}
		}

		unset($arrayElements, $comparableTo);
		return TRUE;
	}

	final public function compareTo($arg1, $arg2, $strict = FALSE) {

		$return = FALSE;
		$typeArg1 =& gettype(&$arg1);
		$typeArg2 =& gettype(&$arg2);

		if($typeArg1 !== $typeArg2) {

			return $return;
		}

		if($typeArg1 === 'string') {

			($strict === TRUE) ? $return =& strcmp(&$arg1, &$arg2) : $return =& strcasecmp(&$arg1, &$arg2);
		}

		else {

			switch(TRUE) {

				case ($arg1 == $arg2 || ($strict === TRUE && $arg1 === $arg2)):
					break;
				case ($arg1 > $arg2):
					$return = -1;
					break;
				case '<':
					($arg1 < $arg3) ? $return = TRUE : $return = FALSE;
					break;
				case '!=':
					($arg1 != $arg3) ? $return = TRUE : $return = FALSE;
					break;
				case '!==':
					($arg1 !== $arg3) ? $return = TRUE : $return = FALSE;
					break;
				case '>=':
					($arg1 >= $arg3) ? $return = TRUE : $return = FALSE;
					break;
				case '<=':
					($arg1 <= $arg3) ? $return = TRUE : $return = FALSE;
					break;
			}
		}

		return $return;
	}

	/*
	 * $arg1: Variable compare to.
	 * $arg2: Variable compare with
	 * $arg3: Type to compare
	 * $arg4: Bool to compare with reference.
	 */

	final public function compare($arg1 = NULL, $arg2 = NULL, $arg3 = NULL, $arg4 = FALSE) {

		$numArgs = 0;
		$numArgs =& func_num_args();
		$typeToCompare = '==';
		$return = NULL;

		if($numArgs < 3 && $numArgs > 0 && $this->numArgs > 0) {

			if($arg4 === TRUE) {

				$arg1 =& $this->variable;
			}

			else {

				$arg1 = $this->variable;
			}
		}

		elseif($numArgs === 3) {

			$typeToCompare =& $arg2;
		}

		else {

			return FALSE;
		}

		switch($typeToCompare) {

			case 'equalto':
			case '==':
				($arg1 == $arg3) ? $return = TRUE : $return = FALSE;
				break;
			case 'strictequalto':
			case '===':
				($arg1 === $arg3) ? $return = TRUE : $return = FALSE;
				break;
			case 'isgreater':
			case '>':
				($arg1 > $arg3) ? $return = TRUE : $return = FALSE;
				break;
			case 'isless':
			case '<':
				($arg1 < $arg3) ? $return = TRUE : $return = FALSE;
				break;
			case 'notequal':
			case '!=':
				($arg1 != $arg3) ? $return = TRUE : $return = FALSE;
				break;
			case 'strictnotequal':
			case '!==':
				($arg1 !== $arg3) ? $return = TRUE : $return = FALSE;
				break;
			case 'greaterorequal':
			case '>=':
				($arg1 >= $arg3) ? $return = TRUE : $return = FALSE;
				break;
			case 'lessorequal':
			case '<=':
				($arg1 <= $arg3) ? $return = TRUE : $return = FALSE;
				break;
		}

		if($return === FALSE && $this->throws === TRUE) {

			$this->throwException('Throw new Exception: \Core\Libs\Cast\Cast::compare().');
		}

		unset($arg1, $arg2, $arg3, $arg4);
		return $return;
	}

	final public function typeEquals() {

		$arrayParams = array();
		$numArgs = 0;
		$numArgs =& func_num_args();
		$arrayParams =& func_get_args();
		$typeToCompare = 'string';

		if($numArgs < 2) {

			if($numArgs === 1) {

				$typeToCompare =& $arrayParams[0];

				if($typeToCompare === 'int') {

					$typeToCompare = 'integer';
				}

				elseif($typeToCompare === 'float' || $typeToCompare === 'numeric') {

					$typeToCompare = 'double';
				}

				elseif($this->variableType === 'NULL') {

					$typeToCompare =& strtoupper(&$typeToCompare);
				}
			}

			if($this->variableType === $typeToCompare) {

				unset($arrayParams);
				return TRUE;
			}

			elseif($typeToCompare === 'numeric' && is_numeric(&$this->variable)) {

				unset($arrayParams);
				return TRUE;
			}

			elseif($this->variableType === 'object' && $this->variable instanceof $typeToCompare) {

				unset($arrayParams);
				return TRUE;
			}

			unset($arrayParams);
			return FALSE;
		}

		elseif($numArgs === 2) {

			$variableType =& gettype($arrayParams[0]);
			$typeToCompare =& $arrayParams[1];

			if($typeToCompare === 'int') {

				$typeToCompare = 'integer';
			}

			elseif($typeToCompare === 'float') {

				$typeToCompare = 'double';
			}

			elseif($variableType === 'NULL') {

				$typeToCompare =& strtoupper(&$typeToCompare);
			}

			if($variableType === $typeToCompare) {

				unset($arrayParams);
				return TRUE;
			}

			elseif($typeToCompare === 'numeric' && is_numeric(&$arrayParams[0])) {

				unset($arrayParams);
				return TRUE;
			}

			elseif($variableType === 'object' && $arrayParams[0] instanceof $typeToCompare) {

				unset($arrayParams);
				return TRUE;
			}

			unset($arrayParams);
			return FALSE;
		}

		unset($arrayParams);
		return FALSE;
	}

	final public function equals() {

		$arrayParams = array();
		$numArgs = 0;
		$numArgs =& func_num_args();
		$arrayParams =& func_get_args();
		$throws = $this->throws;
		$this->throws = FALSE;

		if($numArgs === 1) {

			$variableType =& gettype(&$this->variable);
			$variableCompare =& $arrayParams[0];

			if($variableType === 'boolean' || $variableType === 'NULL') {

				$variableCompare = $this->casting($variableCompare, $variableType);

				if($this->variable === $variableCompare) {

					unset($arrayParams, $variableCompare);
					$this->throws = $throws;
					return TRUE;
				}
			}

			elseif(($variableType === 'integer' && preg_match('/[^+|-|\\][^0-9]+/', &$variableCompare)) || ($variableType === 'double' && !is_numeric(&$variableCompare))) {

				$this->throws = $throws;
				return FALSE;
			}

			else {

				if($this->variable == $variableCompare) {

					unset($arrayParams, $variableCompare);
					$this->throws = $throws;
					return TRUE;
				}
			}

			unset($arrayParams, $variableCompare);
			$this->throws = $throws;
			return FALSE;
		}

		elseif($numArgs > 1) {

			if($numArgs === 2 && $this->numArgs > 0) {

				$this->strict = TRUE;
			}

			elseif($numArgs === 3) {

				$this->strict = TRUE;
			}

			$variable =& $arrayParams[0];
			$this->variableType =& gettype(&$variable);
			$variableCompare =& $arrayParams[1];
			$variableType = $this->variableType;

			if($variableType === 'boolean' || $variableType === 'NULL') {

				$variableCompare = $this->casting($variableCompare, $variableType);

				if($variable === $variableCompare) {

					unset($arrayParams, $variable, $variableCompare);
					$this->throws = $throws;
					return TRUE;
				}
			}

			elseif(($variableType === 'integer' && preg_match('/[^+|-|\\][^0-9]+/', &$variableCompare)) || ($variableType === 'double' && !is_numeric(&$variableCompare))) {

				$this->throws = $throws;
				return FALSE;
			}

			else {

				if($variable == $variableCompare) {

					unset($arrayParams, $variable, $variableCompare);
					$this->throws = $throws;
					return TRUE;
				}
			}

			unset($arrayParams, $variable, $variableCompare);
			$this->throws = $throws;
			return FALSE;
		}

		unset($arrayParams);
		$this->throws = $throws;
		return TRUE;
	}
}
?>